/**
 * Copyright 2010-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.mybatis.spring.annotation;

import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.mybatis.spring.mapper.MapperFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.Import;

/**
 * Use this annotation to register MyBatis mapper interfaces when using Java Config. It performs when same work as
 * {@link MapperScannerConfigurer} via {@link MapperScannerRegistrar}.
 *
 * <p>
 * Either {@link #basePackageClasses} or {@link #basePackages} (or its alias {@link #value}) may be specified to define
 * specific packages to scan. Since 2.0.4, If specific packages are not defined, scanning will occur from the package of
 * the class that declares this annotation.
 *
 * <p>
 * Configuration example:
 * </p>
 *
 * <pre class="code">
 * &#064;Configuration
 * &#064;MapperScan("org.mybatis.spring.sample.mapper")
 * public class AppConfig {
 *
 *   &#064;Bean
 *   public DataSource dataSource() {
 *     return new EmbeddedDatabaseBuilder().addScript("schema.sql").build();
 *   }
 *
 *   &#064;Bean
 *   public DataSourceTransactionManager transactionManager() {
 *     return new DataSourceTransactionManager(dataSource());
 *   }
 *
 *   &#064;Bean
 *   public SqlSessionFactory sqlSessionFactory() throws Exception {
 *     SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
 *     sessionFactory.setDataSource(dataSource());
 *     return sessionFactory.getObject();
 *   }
 * }
 * </pre>
 *
 * @author Michael Lanyon
 * @author Eduardo Macarron
 *
 * @since 1.2.0
 * @see MapperScannerRegistrar
 * @see MapperFactoryBean
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
/**
 * mybatis在注解MapperScan中是提供了@Import的方式来与spring进行整合，@Import在spring中是spring提供了一种扩展点之一
 * spring在扫描的过程中如果发现是@Import的注解，那么@Import会分为三种类型，普通的import、importSelector和ImportBeanDefinitionRegistrar 类型
 * 1.普通类型，直接注册成一个BeanDefinition（可能是普通的bean，可能是特殊的bean，比如后置处理器）； 2.ImportSelector类型，这种类型会提供一个回调方法返回一个类的数组；
 * 3.ImportBeanDefinitionRegistrar类型就是可以用来注册BeanDefinition，扫描BeanDefinition；
 * mybatis在早期的整合版本中就是使用的ImportBeanDefinitionRegistrar的方式进行整合的，在ImportBeanDefinitionRegistrar中
 * mybatis自定义了一个扫描器，继承了spring的扫描器ClassPathMapperScanner来重写而来spring的扫描验证逻辑
 * 这样就可以很简单的利用了spring的扫描功能将我们的bMapper扫描出来（Mapper是接口，正常的spring在扫描的过程中是不可能扫描出来的
 * 但是mybatis重写了spring的扫描判断逻辑，mybatis判断如果是一个接口就返回true，然后扫描的到的BeanDefinition是一个
 * 接口，spring最后是没有办法去创建对象的，所以这个时候mybatis把这些BeanDefinition的属性改变了
 * mybatis使用spring的factoryBean来创建一个代理对象，被代理的是Mapper，这样放入BeanDefinition中的beanclass就是
 * 代理对象，这与就改变了BeanDefinition，所以spring在bean生命周期中就可以创建对象了
 *
 * 在现在的版本中，mybatis也是采用这种逻辑进行整合的，但是spring还利用了一种新的方式，就是以前只能通过@Import的方式导入一个
 * Registr的方式来整合，定义一个注解MapperScan来指定扫描的属性，而现在还提供了一种BeanFactory的后置处理器
 * 我们知道beanFactory的后置处理器的子接口BeanDefinitionRegistryPostProcessor是可以对BeanDefinition
 * 进行注册的，所以的新的方式在核心的逻辑上没有改变，但是在思想上有了改变，就是如果采用的是@Import一个Registr的方式的话
 * 那么这个Registr会注册一个BeanDefinition，而这个BeanDefinition就是BeanDefinitionRegistryPostProcessor类型
 * 而spring在扫描的过程中会不断去调用BeanDefinition中是bean工厂的后置处理器的，所以spring也会找到被注册的这个
 * BeanDefinition，而这个BeanDefinition不是一个普通的BeanDefinition，而是一个BeanDefinitionRegistryPostProcessor
 * 类型的BeanDefinition，所以spring就会去调用它的后置处理器，从而进行核销的逻辑，也就是上面所写的逻辑
 */
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {

  /**
   * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation declarations e.g.:
   * {@code @MapperScan("org.my.pkg")} instead of {@code @MapperScan(basePackages = "org.my.pkg"})}.
   *
   * @return base package names
   */
  /**
   * 定义扫描包的路径
   */
  String[] value() default {};

  /**
   * Base packages to scan for MyBatis interfaces. Note that only interfaces with at least one method will be
   * registered; concrete classes will be ignored. 也是扫码包的路径
   * 
   * @return base package names for scanning mapper interface
   */
  String[] basePackages() default {};

  /**
   * Type-safe alternative to {@link #basePackages()} for specifying the packages to scan for annotated components. The
   * package of each class specified will be scanned.
   * <p>
   * Consider creating a special no-op marker class or interface in each package that serves no purpose other than being
   * referenced by this attribute. 可以定义一个class，从class获取包的路径
   * 
   * @return classes that indicate base package for scanning mapper interface
   */
  Class<?>[] basePackageClasses() default {};

  /**
   * The {@link BeanNameGenerator} class to be used for naming detected components within the Spring container.
   *
   * @return the class of {@link BeanNameGenerator}
   */
  // bean的名字生成器，默认使用的是BeanNameGenerator
  Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

  /**
   * This property specifies the annotation that the scanner will search for.
   * <p>
   * The scanner will register all interfaces in the base package that also have the specified annotation.
   * <p>
   * Note this can be combined with markerInterface. HE
   * 
   * @return the annotation that the scanner will search for
   */
  // 定义注解
  Class<? extends Annotation> annotationClass() default Annotation.class;

  /**
   * This property specifies the parent that the scanner will search for.
   * <p>
   * The scanner will register all interfaces in the base package that also have the specified interface class as a
   * parent.
   * <p>
   * Note this can be combined with annotationClass.
   *
   * @return the parent that the scanner will search for
   */
  Class<?> markerInterface() default Class.class;

  /**
   * Specifies which {@code SqlSessionTemplate} to use in the case that there is more than one in the spring context.
   * Usually this is only needed when you have more than one datasource.
   *
   * @return the bean name of {@code SqlSessionTemplate}
   */
  // 定义sqlSessionTemplate的名字，如果定义了，spring会从容器中去取一个名字来使用
  String sqlSessionTemplateRef() default "";

  /**
   * Specifies which {@code SqlSessionFactory} to use in the case that there is more than one in the spring context.
   * Usually this is only needed when you have more than one datasource.
   *
   * @return the bean name of {@code SqlSessionFactory}
   */
  String sqlSessionFactoryRef() default "";

  /**
   * Specifies a custom MapperFactoryBean to return a mybatis proxy as spring bean.
   * 代理的factorybean，默认是MapperFactoryBean，可以自己定义
   * 
   * @return the class of {@code MapperFactoryBean}
   */
  Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;

  /**
   * Whether enable lazy initialization of mapper bean.
   *
   * <p>
   * Default is {@code false}.
   * </p>
   * 是否是延迟加载
   * 
   * @return set {@code true} to enable lazy initialization
   * @since 2.0.2
   */
  String lazyInitialization() default "";

  /**
   * Specifies the default scope of scanned mappers.
   *
   * <p>
   * Default is {@code ""} (equiv to singleton).
   * </p>
   *
   * @return the default scope
   */
  String defaultScope() default AbstractBeanDefinition.SCOPE_DEFAULT;

}
